#include "binary-in-stream.hh"
#include "binary-out-stream.hh"
#include "lom-binary-in-stream.hh"
#include "lom-binary-out-stream.hh"
#include "lom-value-factory.hh"
#include "lom-class-manager.hh"
#include "tests.hh"
#include "lom-instance.hh"
#include "lom-class.hh"
#include "lom-instance-manager.hh"
#include "lom-stream-handle-map.hh"


using namespace licada;
using namespace lom;
using namespace std;



class LomStreamTest : public TestBase
{
    //All the supported property types
    bool boolVal;
    double doubleVal;
    uint8 u8Val;
    uint16 u16Val;
    uint32 u32Val;
    int64 i64Val;
    TDoubleVector dVect;
    TInt64Vector i64Vect;
    TString strVal;
    TStringVector strVect;
    TUInt32Vector u32Vect;
    TDataVector dataVect;
    TNameStr nameStr;
    LomClass lomCls;
    LomInstance* testInst;

    LomValueFactory valueFactory;
    LomInstanceManager instanceMgr;
    LomClassManager classMgr;
    LomEventSystem eventSystem;

    TDataVector streamVect;
    VectorBinaryOutStream outProxy;
    VectorBinaryInStream inProxy;
    LomBinaryOutStream outStream;
    LomBinaryInStream inStream;
    LomIdentityIncomingHandleMap inMap;
    LomIdentityOutgoingHandleMap outMap;
    
    
public:
    virtual TNameType getName() { return "LomStreamTest"; }
    
    virtual void setup() {
	inProxy.setup( &streamVect );
	outProxy.setup( &streamVect );
	inStream.setup( &inProxy, &valueFactory, &classMgr, &inMap );
	outStream.setup( &outProxy, &outMap );
	instanceMgr.reset();
	classMgr.reset();
	valueFactory.setup( &instanceMgr );
	
	boolVal = true;
	doubleVal = -50;
	u8Val = 20;
	u16Val = 20000;
	u32Val = 567;
	i64Val = -1;
	dVect.push_back( 1 );
	dVect.push_back( 2 );
	dVect.push_back( -1 );
	dataVect.push_back( 100 );
	dataVect.push_back( 255 );
	u32Vect.push_back( 1 );
	u32Vect.push_back( 100 );
	i64Vect.push_back( 1 );
	i64Vect.push_back( 2 );
	i64Vect.push_back( -1 );
	strVal.push_back( 'a' );
	strVal.push_back( 'b' );
	strVal.push_back( 'c' );
	strVect.push_back( strVal );
	strVect.push_back( strVal );
	nameStr = "chris";
	lomCls.initialize( "test_class", &valueFactory );
	lomCls.addProperty( LomProperty( "prop1", EDataTypeInt64 ) );
	lomCls.addProperty( LomProperty( "prop2", EDataTypeDouble ), LomDouble(20));
	classMgr.addClass(lomCls);
	testInst  = new LomInstance(classMgr.getClass("test_class")
					    , &valueFactory
					    , &instanceMgr );
	testInst->addRef();
	testInst->setEventHandler( &eventSystem );
	testInst->set( "prop1", LomInt64(42) );
    }
    
    virtual void testPrecondition() {
	TEST_EQ( true, boolVal );
	TEST_EQ( -50, doubleVal );
	TEST_EQ( 20, u8Val );
	TEST_EQ( 20000, u16Val );
	TEST_EQ( 567, u32Val );
	TEST_EQ( -1, i64Val );
	TEST_EQ( 3, dVect.size() );
	TEST_EQ( 1, dVect[0] );
	TEST_EQ( 2, dVect[1] );	
	TEST_EQ( -1, dVect[2] );
	TEST_EQ( 2, dataVect.size() );
	TEST_EQ( 100, dataVect[0] );
	TEST_EQ( 255, dataVect[1] );
	TEST_EQ( 2, u32Vect.size() );
	TEST_EQ( 1, u32Vect[0] );
	TEST_EQ( 100, u32Vect[1] );
	TEST_EQ( 3, i64Vect.size() );
	TEST_EQ( 1, i64Vect[0] );
	TEST_EQ( 2, i64Vect[1] );
	TEST_EQ( -1, i64Vect[2] );
	TEST_EQ( 3, strVal.size() );
	TEST_EQ( 'a', strVal[0] );
	TEST_EQ( 'b', strVal[1] );
	TEST_EQ( 'c', strVal[2] );
	TEST_EQ( 2, strVect.size() );
	TEST_EQ( 3, strVect[0].size() );
	TEST_EQ( 'a', strVect[0][0] );
	TEST_EQ( 'b', strVect[0][1] );
	TEST_EQ( 'c', strVect[0][2] );
	TEST_EQ( 3, strVect[1].size() );
	TEST_EQ( 'a', strVect[1][0] );
	TEST_EQ( 'b', strVect[1][1] );
	TEST_EQ( 'c', strVect[1][2] );
	TEST_EQ( "chris", nameStr );
	TEST_EQ( "test_class", lomCls.getName() );
	TEST_EQ( 2, lomCls.getProperties().size() );
	TEST_EQ( "prop1", lomCls.getProperties()[0].name );
	TEST_EQ( "prop2", lomCls.getProperties()[1].name );
	TEST_EQ( EDataTypeInt64, lomCls.getProperties()[0].type );
	TEST_EQ( EDataTypeDouble, lomCls.getProperties()[1].type );
	TEST_EQ( 2, lomCls.getDefaults().size() );
	TEST_EQ( NULL, lomCls.getDefaults()[0] );
	TEST_EQ( EDataTypeDouble, lomCls.getDefaults()[1]->getDataType() );
	TEST_EQ( 20, lomCls.getDefaults()[1]->getDouble() );
	TEST_EQ( "test_class", testInst->getClass()->getName() );
	TEST_EQ( 2, testInst->getValues().size() );
	TEST_EQ( 42, testInst->getValues()[0]->getInt64() );
	TEST_EQ( 20, testInst->getValues()[1]->getDouble() );
    }
    
    virtual void doOperation() {
	outStream << boolVal;
	outStream << doubleVal;
	outStream << u8Val;
	outStream << u16Val;
	outStream << u32Val;
	outStream << i64Val;
	outStream << dVect;
	outStream << i64Vect;
	outStream << u32Vect;
	outStream << dataVect;
	outStream << strVal;
	outStream << strVect;
	outStream << nameStr.c_str();
	outStream << lomCls;
	outStream << *testInst;
	boolVal = false;
	doubleVal = 0;
	u8Val = 0;
	u16Val = 0;
	u32Val = 0;
	i64Val = 0;
	dVect.clear();
	i64Vect.clear();
	u32Vect.clear();
	dataVect.clear();
	strVal.clear();
	strVect.clear();
	nameStr.clear();
	lomCls.initialize( "", &valueFactory );
	TEST_EQ( "", lomCls.getName() );
	testInst->initialize( NULL, &valueFactory, &instanceMgr );
	TEST_EQ( NULL, testInst->getClass() );
	inStream >> boolVal;
	inStream >> doubleVal;
	inStream >> u8Val;
	inStream >> u16Val;
	inStream >> u32Val;
	inStream >> i64Val;
	inStream >> dVect;
	inStream >> i64Vect;
	inStream >> u32Vect;
	inStream >> dataVect;
	inStream >> strVal;
	inStream >> strVect;
	inStream >> nameStr;
	inStream >> lomCls;
	inStream >> *testInst;
    }
    
    virtual void testPostcondition() {
	testPrecondition();
    }
    
    virtual void teardown() {
	instanceMgr.reset();
	classMgr.reset();
	lomCls.initialize( "", NULL );
    }
    
};

int main( int , char**  )
{
    runTest<LomStreamTest>();
}
